package jsonmarshal

import(
	"bytes"
	"encoding"
	"fmt"
	"io"
	"reflect"
	"sort"
	"strings"
	"unicode"
)
type sortValues []reflect.Value

func (p sortValues) Len() int {return len(p)}
func (p sortValues) Less(i, j int) bool {return p[i].String()<p[j].String()}
func (p sortValues) Swap(i, j int) {p[i], p[j] = p[j], p[i]}
type RawMessage []byte

type Marshaler interface{
	MarshaleJson() ([]byte, error)
}
type Encoder struct{
	w io.Writer
}
func NewEncoder(w io.Writer) *Encoder{
	return &Encoder{w}
}
func JsonMarshal(v interface{}) ([]byte, error){
	return EncodeBytes(v)
}
func (e *Encoder)Encode(val interface{}) error{
	return encodeValue(e.w, reflect.ValueOf(val))
}
func EncodeString(val interface{}) (string, error){
	buf := new(bytes.Buffer)
	e := NewEncoder(buf)
	if err := e.Encode(val); err != nil{
		return "", err
	}
	return buf.String(), nil
}
func EncodeBytes(val interface{}) ([]byte, error){
	buf := new(bytes.Buffer)
	e := NewEncoder(buf)
	if err := e.Encode(val); err != nil{
		return nil, err
	}
	return buf.Bytes(), nil
}
func isNilValue(v reflect.Value) bool{
	return (v.Kind() == reflect.Interface || v.Kind() == reflect.Ptr) && v.IsNil()
}
func encodeValue(w io.Writer, val reflect.Value) error{
	marshaler, textMarshaler, v := indirectEncodeValue(val)
	if marshaler != nil {
		bytes, err := marshaler.MarshaleJson()
		if err != nil{
			return err
		}
		_, err = w.Write(bytes)
		return err
	}
	if textMarshaler != nil{
		bytes, err := textMarshaler.MarshalText()
		if err != nil{
			return err
		}
		_, err = fmt.Fprintf(w, "%d:%s", len(bytes),bytes)
		return err
	}
	if !v.IsValid(){
		return nil
	}
	if rm, ok := v.Interface().(RawMessage); ok{
		_, err := io.Copy(w, bytes.NewReader(rm))
		return err
	}
	switch v.Kind(){
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		_, err := fmt.Fprintf(w, "%d", v.Int())
		return err
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		_, err := fmt.Fprintf(w, "%d", v.Uint())
		return err
	case reflect.Float32, reflect.Float64:
		_, err := fmt.Fprintf(w, "%.3f", v.Float())
		return err
	case reflect.Bool:
		i := 0
		if v.Bool(){
			i = 1
		}
		if i == 1 {
			_, err :=fmt.Fprintf(w, "true")
			return err
		}else {
			_, err :=fmt.Fprintf(w, "false")
			return err
		}
	case reflect.String:
		fmt.Fprint(w, "\"")
		_, err := fmt.Fprintf(w, "%s",v.String())
		fmt.Fprint(w, "\"")
		return err
	case reflect.Slice, reflect.Array:
		if byteSlice, ok := val.Interface().([]byte); ok {
			_, err := fmt.Fprintf(w, "%d:", len(byteSlice))
			if err == nil{
				_, err = w.Write(byteSlice)
			}
			return err
		}
		if _, err := fmt.Fprint(w); err != nil{
			return err
		}
		fmt.Fprint(w, "[")
		for i := 0; i < v.Len(); i++{
			if err := encodeValue(w, v.Index(i)); err != nil{
				return err
			}
			if i != v.Len()-1{
				fmt.Fprint(w, ",")
			}
		}
		fmt.Fprint(w, "]")
		_, err := fmt.Fprint(w)
		return err
	case reflect.Map:
		if _, err := fmt.Fprint(w); err != nil{
			return err
		}
		var (
			keys sortValues = v.MapKeys()
			mval reflect.Value
		)
		sort.Sort(keys)
		fmt.Fprint(w, "{")
		for i := range keys{
			mval = v.MapIndex(keys[i])
			if isNilValue(mval){
				continue
			}
			switch keys[i].Kind(){
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
				fmt.Fprint(w, "\"")
			}
			if err := encodeValue(w, keys[i]); err != nil{
				return err
			}
			switch keys[i].Kind(){
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
				fmt.Fprint(w, "\"")
			}
			fmt.Fprint(w, ":")
			if err := encodeValue(w, mval); err != nil{
				return err
			}
			if i != keys.Len()-1{
				fmt.Fprint(w, ",")
			}
		} 
		fmt.Fprint(w, "}")
		_, err := fmt.Fprint(w)
		return err
	case reflect.Struct:
		if _, err := fmt.Fprint(w); err != nil{
			return err
		}
		dict := make(dictionary, 0, v.NumField())
		dict, err := readStruct(dict, v)
		if err != nil{
			return err
		}
		sort.Sort(dict)
		fmt.Fprint(w, "{")
		for i, def := range dict{
			err := encodeValue(w, reflect.ValueOf(def.key))
			if err != nil{
				return err
			}
			fmt.Fprint(w, ":")
			err = encodeValue(w, def.value)
			if err != nil{
				return err
			}
			if i != dict.Len()-1{
				fmt.Fprint(w, ",")
			}
		}
		fmt.Fprint(w, "}")
		_, err = fmt.Fprint(w)
		return err
	}
	return fmt.Errorf("Can't encode type: %s", v.Type())
}
func indirectEncodeValue(v reflect.Value)(Marshaler, encoding.TextMarshaler, reflect.Value){
	if v.Kind() != reflect.Ptr &&v.Type().Name() != ""&& v.CanAddr(){
		v = v.Addr()
	}
	for {
		if v.Kind() == reflect.Ptr && v.IsNil(){
			break
		}
		vi := v.Interface()
		if m, ok := vi.(Marshaler); ok{
			return m, nil, reflect.Value{}
		}
		if m, ok := vi.(encoding.TextMarshaler); ok{
			return  nil, m, reflect.Value{}
		}
		if v.Kind() != reflect.Ptr{
			break
		}
		v = v.Elem()
	}
	return nil, nil, indirect(v, false)
}
func indirect(v reflect.Value, alloc bool) reflect.Value {
	for {
		switch v.Kind() {
		case reflect.Interface:
			if v.IsNil() {
				if !alloc {
					return reflect.Value{}
				}
				return v
			}

		case reflect.Ptr:
			if v.IsNil() {
				if !alloc {
					return reflect.Value{}
				}
				v.Set(reflect.New(v.Type().Elem()))
			}

		default:
			return v
		}

		v = v.Elem()
	}
}
type definition struct{
	key string
	value reflect.Value
}
type dictionary []definition

func (d dictionary)Len() int {return len(d)}
func (d dictionary)Less(i, j int) bool {return d[i].key < d[j].key}
func (d dictionary)Swap(i, j int) {d[i], d[j] = d[j], d[i]}

func readStruct(dict dictionary, v reflect.Value) (dictionary, error){
	t := v.Type()
	var (
		fieldValue reflect.Value
		rkey string
	)
	for i:=0; i<t.NumField(); i++ {
		key := t.Field(i)
		rkey = key.Name
		fieldValue = v.FieldByIndex(key.Index)
		if !fieldValue.CanInterface(){
			continue
		}
		if isNilValue(fieldValue){
			continue
		}
		tagValue := key.Tag.Get("jsonmarshal")
		if tagValue != ""{
			if tagValue == "-"{
				continue
			}
			name, options := parseTag(tagValue)
			if options.Contains("omitempty")&& isEmptyValue(fieldValue){
				continue
			}
			if isValidTag(name){
				rkey = name
			}
		}
		if key.Anonymous && key.Type.Kind() == reflect.Struct && tagValue == ""{
			var err error
			dict, err = readStruct(dict, fieldValue)
			if err != nil{
				return nil, err
			}
		} else {
			dict = append(dict, definition{rkey, fieldValue})
		} 
	}
	return dict, nil
}
type tagOptions string
func parseTag(tag string) (string, tagOptions) {
	if idx := strings.Index(tag, ","); idx != -1 {
		return tag[:idx], tagOptions(tag[idx+1:])
	}
	return tag, tagOptions("")
}
func (options tagOptions) Contains(optionName string) bool {
	s := string(options)
	for s != "" {
		var next string
		i := strings.Index(s, ",")
		if i >= 0 {
			s, next = s[:i], s[i+1:]
		}
		if s == optionName {
			return true
		}
		s = next
	}
	return false
}

func isValidTag(key string) bool {
	if key == "" {
		return false
	}
	for _, c := range key {
		if c != ' ' && c != '$' && c != '-' && c != '_' && c != '.' && !unicode.IsLetter(c) && !unicode.IsDigit(c) {
			return false
		}
	}
	return true
}

func matchName(key string) func(string) bool {
	return func(s string) bool {
		return strings.ToLower(key) == strings.ToLower(s)
	}
}

func isEmptyValue(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
		return v.Len() == 0
	case reflect.Bool:
		return !v.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return v.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return v.Float() == 0
	case reflect.Interface, reflect.Ptr:
		return v.IsNil()
	}

	return false
}